# -*- coding: utf-8 -*-
"""
DTM验证工具 - 数据处理模块
负责Excel文件读取、数据验证和预处理
"""

import os
import pandas as pd
import numpy as np
from typing import Tuple, List, Optional
from config_manager import Constants


class ExcelDataProcessor:
    """Excel数据处理器"""

    def __init__(self):
        self.constants = Constants()

    def read_vertices_from_excel(self, file_path: str) -> np.ndarray:
        """
        从Excel文件读取顶点坐标数据

        Args:
            file_path: Excel文件路径

        Returns:
            np.ndarray: 顶点坐标数组，形状为(n, 3)，列为[X, Y, Z]

        Raises:
            FileNotFoundError: 文件不存在
            ValueError: 数据格式错误或包含非数值数据
        """
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"文件不存在: {file_path}")

        try:
            # 读取Excel文件，不包含表头
            dataframe = pd.read_excel(file_path, header=None)

            if dataframe.empty:
                raise ValueError(self.constants.ERROR_MESSAGES["empty_data"])

            # 提取坐标列（第2、3、4列，索引为1、2、3）
            coordinate_data = dataframe.iloc[
                :, self.constants.EXCEL_COORDINATE_COLUMNS
            ].copy()

            # 转换为数值类型，无法转换的设为NaN
            numeric_data = coordinate_data.apply(pd.to_numeric, errors="coerce")

            # 交换X和Y坐标以符合标准坐标系
            numeric_data[[1, 2]] = numeric_data[[2, 1]].values

            # 检查并报告非数值数据
            self._validate_numeric_data(numeric_data, coordinate_data)

            # 移除包含NaN的行
            clean_data = numeric_data.dropna()

            if clean_data.empty:
                raise ValueError(self.constants.ERROR_MESSAGES["empty_data"])

            vertices = clean_data.values.astype(np.float64)

            # 验证坐标范围
            self._validate_coordinate_range(vertices)

            return vertices

        except Exception as e:
            if isinstance(e, (FileNotFoundError, ValueError)):
                raise
            else:
                raise ValueError(f"读取Excel文件时发生错误: {str(e)}")

    def _validate_numeric_data(
        self, numeric_data: pd.DataFrame, original_data: pd.DataFrame
    ) -> None:
        """
        验证数值数据的有效性

        Args:
            numeric_data: 转换后的数值数据
            original_data: 原始数据

        Raises:
            ValueError: 发现非数值数据时抛出
        """
        invalid_rows = numeric_data.isnull().any(axis=1)

        if invalid_rows.any():
            error_locations = []
            for idx, row in original_data[invalid_rows].iterrows():
                bad_columns = []
                for col_idx, value in enumerate(row):
                    if pd.isna(numeric_data.iloc[idx, col_idx]):
                        bad_columns.append(str(col_idx + 2))  # 列号从2开始

                error_locations.append(f"第{idx + 1}行[{','.join(bad_columns)}列]")

            error_message = (
                f"{self.constants.ERROR_MESSAGES['non_numeric_data']}\n"
                f"错误位置：\n" + "\n".join(error_locations)
            )
            raise ValueError(error_message)

    def _validate_coordinate_range(self, vertices: np.ndarray) -> None:
        """
        验证坐标值是否在合理范围内

        Args:
            vertices: 顶点坐标数组

        Raises:
            ValueError: 坐标超出合理范围时抛出
        """
        min_val = self.constants.MIN_COORDINATE_VALUE
        max_val = self.constants.MAX_COORDINATE_VALUE

        if (vertices < min_val).any() or (vertices > max_val).any():
            raise ValueError(f"坐标值超出合理范围 [{min_val}, {max_val}]")

    def get_data_statistics(self, vertices: np.ndarray) -> dict:
        """
        获取数据的统计信息

        Args:
            vertices: 顶点坐标数组

        Returns:
            dict: 包含统计信息的字典
        """
        if vertices.size == 0:
            return {}

        return {
            "vertex_count": len(vertices),
            "x_range": (np.min(vertices[:, 0]), np.max(vertices[:, 0])),
            "y_range": (np.min(vertices[:, 1]), np.max(vertices[:, 1])),
            "z_range": (np.min(vertices[:, 2]), np.max(vertices[:, 2])),
            "x_mean": np.mean(vertices[:, 0]),
            "y_mean": np.mean(vertices[:, 1]),
            "z_mean": np.mean(vertices[:, 2]),
            "centroid": np.mean(vertices, axis=0),
        }


class DataValidator:
    """数据验证器"""

    @staticmethod
    def validate_file_format(file_path: str) -> bool:
        """
        验证文件格式是否支持

        Args:
            file_path: 文件路径

        Returns:
            bool: 文件格式是否支持
        """
        _, ext = os.path.splitext(file_path.lower())
        return ext in Constants.EXCEL_FILE_EXTENSIONS

    @staticmethod
    def validate_vertices_for_triangulation(vertices: np.ndarray) -> bool:
        """
        验证顶点数据是否足够进行三角剖分

        Args:
            vertices: 顶点坐标数组

        Returns:
            bool: 是否满足三角剖分条件
        """
        return len(vertices) >= 3

    @staticmethod
    def validate_height_value(height: float) -> bool:
        """
        验证高程值是否有效

        Args:
            height: 高程值

        Returns:
            bool: 高程值是否有效
        """
        return (
            isinstance(height, (int, float))
            and not np.isnan(height)
            and not np.isinf(height)
        )


class BatchDataProcessor:
    """批量数据处理器"""

    def __init__(self):
        self.excel_processor = ExcelDataProcessor()
        self.validator = DataValidator()
        self.processing_results: List[dict] = []

    def process_files_batch(self, file_configs: List[tuple]) -> List[dict]:
        """
        批量处理多个文件

        Args:
            file_configs: 文件配置列表，每个元素为(file_path, fixed_height)

        Returns:
            List[dict]: 处理结果列表
        """
        results = []

        for file_path, fixed_height in file_configs:
            try:
                result = self.process_single_file(file_path, fixed_height)
                results.append(result)
            except Exception as e:
                error_result = {
                    "file_path": file_path,
                    "success": False,
                    "error": str(e),
                    "vertices": None,
                    "statistics": None,
                }
                results.append(error_result)

        self.processing_results = results
        return results

    def process_single_file(self, file_path: str, fixed_height: float) -> dict:
        """
        处理单个文件

        Args:
            file_path: 文件路径
            fixed_height: 基准高程

        Returns:
            dict: 处理结果
        """
        # 验证文件格式
        if not self.validator.validate_file_format(file_path):
            raise ValueError(f"不支持的文件格式: {file_path}")

        # 验证高程值
        if not self.validator.validate_height_value(fixed_height):
            raise ValueError(f"无效的高程值: {fixed_height}")

        # 读取和处理数据
        vertices = self.excel_processor.read_vertices_from_excel(file_path)
        statistics = self.excel_processor.get_data_statistics(vertices)

        # 验证是否足够进行三角剖分
        if len(vertices) < 3:
            raise ValueError(Constants.ERROR_MESSAGES["insufficient_vertices"])

        return {
            "file_path": file_path,
            "success": True,
            "error": None,
            "vertices": vertices,
            "statistics": statistics,
            "fixed_height": fixed_height,
        }

    def get_batch_summary(self) -> dict:
        """
        获取批量处理的汇总信息

        Returns:
            dict: 汇总信息
        """
        if not self.processing_results:
            return {}

        successful = [r for r in self.processing_results if r["success"]]
        failed = [r for r in self.processing_results if not r["success"]]

        return {
            "total_files": len(self.processing_results),
            "successful_count": len(successful),
            "failed_count": len(failed),
            "success_rate": len(successful) / len(self.processing_results) * 100,
            "failed_files": [r["file_path"] for r in failed],
        }
